﻿
namespace TestFsWeb

open System
open System.IO
open System.Net
open System.Web
open System.Text
open Microsoft.FSharp.Control.WebExtensions
open Newtonsoft.Json

open Helper
open HttpListenerExtend

module DemoQueryString =

    type NameValuePair = {
        Name : string
        Value : string
    }

    type Server(port)=
        do
            if not HttpListener.IsSupported then
                failwith "HttpListener is not supported by this OS"

        let m_listener = new HttpListener()

        do
            m_listener.Prefixes.Add <| sprintf "http://localhost:%d/" port

        // ----------------- public API
        interface IDisposable with
            member this.Dispose() =
                m_listener.Stop()
                m_listener.Close()

        member this.Start() =
            m_listener.Start()

            let rec loop() = async {
                let! received = m_listener.AsyncGetContext() |> Async.Catch
                match received with
                | Choice2Of2 ex ->
                    printfn "failed to receive request context. Error: %s" ex.Message
                | Choice1Of2 context->
                    let request = context.Request

                    // printfn "URL: \n\t%s" request.Url.OriginalString
                    // printfn "Raw URL: \n\t%s" request.RawUrl
                    printfn "QueryString: \n\t%s" request.Url.Query

                    if request.HttpMethod.Equals("get",StringComparison.OrdinalIgnoreCase) then
                        let kvs = request.QueryString
                        let bytes = 
                            kvs.AllKeys
                            |> Seq.map (fun name -> {Name=name;Value=kvs.[name]})
                            |> JsonConvert.SerializeObject
                            |> Encoding.UTF8.GetBytes
                        use response = context.Response
                        do! response.AsyncReply ("application/json",bytes)
                    else
                        use response = context.Response
                        response.StatusCode <- int HttpStatusCode.Forbidden
                        do! response.AsyncReply "only accept 'GET' request"
                return! loop()
            }
            loop() |> Async.StartImmediate

    let client_with_httpwebrequest count port (parameters: seq<string*string>) =
        printfn "######################## HttpWebRequest ########################"

        let make_querystring (parameters : seq<string*string>) =
            let namevalues = HttpUtility.ParseQueryString ""
            parameters
            |> Seq.iter namevalues.Add
            namevalues.ToString()

        async {
            for index = 1 to count do
                let request = 
                    parameters
                    |> make_querystring
                    |> sprintf "http://localhost:%d/querystring?%s" port
                    |> WebRequest.Create

                let! received = request.AsyncGetResponse()
                use response = received :?> HttpWebResponse
                use reader = new StreamReader(response.GetResponseStream())
                let! text = reader.AsyncReadToEnd()

                if response.StatusCode = HttpStatusCode.OK then
                    printfn "********** [%d] CLIENT RECEIVED: **********" index
                    let namevalues = JsonConvert.DeserializeObject<NameValuePair[]>(text)
                    namevalues
                    |> Seq.iter (fun pair -> printfn "[%s]: %s" pair.Name pair.Value)
                else
                    printfn "Error!!!\nCode=%d\nDescription=%s\nMessage=%s" (int response.StatusCode) response.StatusDescription text
        } |> Async.StartImmediate

    let client_with_webclient count port (parameters: seq<string*string>) =
        printfn "######################## WebClient ########################"

        let url = sprintf "http://localhost:%d/querystring" port

        async {
            // the same webclient can be re-used for multiple times
            use client = new WebClient()
            parameters
            |> Seq.iter client.QueryString.Add

            for index = 1 to count do
                try
                    let! json = client.AsyncDownloadString <| new Uri(url)

                    printfn "********** [%d] CLIENT RECEIVED: **********" index
                    let namevalues = JsonConvert.DeserializeObject<NameValuePair[]>(json)
                    namevalues
                    |> Seq.iter (fun pair -> printfn "[%s]: %s" pair.Name pair.Value)
                with
                    | :? WebException as ex->
                        let response = ex.Response :?> HttpWebResponse
                        if response <> null then
                            printfn "Error!!!\nCode=%d\nDescription=%s" (int response.StatusCode) response.StatusDescription
        } |> Async.StartImmediate

module TestDemoQueryString =
    open DemoQueryString

    let private test client_func =
        let port = 5000
        let count = 10
        
        // -------------- start the server
        use server = new Server(port)
        server.Start()
        printfn "server is started"

        pause_for("start the client")
        [1..3]
        |> Seq.map (fun index -> 
                                let key = sprintf "key<%d>" index
                                let value = sprintf "value '%d'" index
                                key,value)
        |> client_func count port

        pause_for("exit")

    let main() =
        // test client_with_httpwebrequest
        test client_with_webclient
        
        
        

